---
title: Guide Documents > S/W Development Kit > E2E Test Functions
---
import { Tabs } from 'nextra/components'

## Outline
<Tabs items={['Configuration', 'Build Script']} defaultIndex={1}>
  <Tabs.Tab>
```typescript filename="nestia.config.ts" copy showLineNumbers {20}
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";

import { YourModule } from "./src/YourModule";

const NESTIA_CONFIG: INestiaConfig = {
  input: async () => {
    const app = await NestFactory.create(YourModule);
    // const app = await NestFactory.create(YourModule, new FastifyAdapter());
    // app.setGlobalPrefix("api");
    // app.enableVersioning({
    //     type: VersioningType.URI,
    //     prefix: "v",
    // })
    return app;
  },
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```bash filename="Terminal" copy showLineNumbers
npx nestia e2e
npx nestia e2e --config nestia.config.ts --project tsconfig.json
```
  </Tabs.Tab>
</Tabs>

Configure [`nestia.config.ts`](#configuration) file and run `npx nestia e2e` command.

Then, `@nestia/sdk` will analyze your NestJS backend server code, and generate both [SDK (Software Development Kit) library](./sdk) for client developers. Also, E2E test functions, utilizing the [SDK library](./sdk), will be automatically generated for corresponding to every API functions.

Here is an example of generated E2E test functions:

```typescript filename="test/api/features/automated/test_api_body_store.ts" showLineNumbers
import typia, { Primitive } from "typia";

import api from "../../../../src/api";
import type { IBbsArticle } from "../../../../src/api/structures/IBbsArticle";

export const test_api_body_store = async (
  connection: api.IConnection,
): Promise<void> => {
  const output = await api.functional.body.store(
    connection,
    typia.random<Primitive<IBbsArticle.IStore>>(),
  );
  typia.assert(output);
};
```

![SDK](https://user-images.githubusercontent.com/13158709/215004990-368c589d-7101-404e-b81b-fbc936382f05.gif)

> Left is server code, and right is e2e test code utilizing SDK library




## Configuration
<Tabs items={[
  <code>nestia.config.ts</code>,
  <code>INestiaConfig.ts</code>,
]}>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {20}
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
// import { FastifyAdapter } from "@nestjs/platform-fastify";

import { YourModule } from "./src/YourModule";

const NESTIA_CONFIG: INestiaConfig = {
  input: async () => {
    const app = await NestFactory.create(YourModule);
    // const app = await NestFactory.create(YourModule, new FastifyAdapter());
    // app.setGlobalPrefix("api");
    // app.enableVersioning({
    //     type: VersioningType.URI,
    //     prefix: "v",
    // })
    return app;
  },
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript copy filename="INestiaConfig.ts" showLineNumbers {67-76}
import type { INestApplication } from "@nestjs/common";
import { OpenApi } from "@samchon/openapi";

/**
 * Definition for the `nestia.config.ts` file.
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export interface INestiaConfig {
  /**
   * Accessor of controller classes.
   *
   * You can specify it within two ways
   *
   *   - Asynchronous function returning `INestApplication` instance
   *   - Specify the path or directory of controller class files
   */
  input:
    | (() => Promise<INestApplication>)
    | INestiaConfig.IInput
    | string[]
    | string;

  /**
   * Output directory that SDK would be placed in.
   *
   * If not configured, you can't build the SDK library.
   */
  output?: string;

  /**
   * Building `swagger.json` is also possible.
   *
   * If not specified, you can't build the `swagger.json`.
   */
  swagger?: INestiaConfig.ISwaggerConfig;

  /**
   * Target directory that SDK distribution files would be placed in.
   *
   * If you configure this property and runs `npx nestia sdk` command,
   * distribution environments for the SDK library would be generated.
   *
   * After the SDK library generation, move to the `distribute` directory,
   * and runs `npm publish` command, then you can share SDK library with
   * other client (frontend) developers.
   *
   * Recommend to use `"packages/api"` value.
   */
  distribute?: string;

  /**
   * Allow simulation mode.
   *
   * If you configure this property to be `true`, the SDK library would be contain
   * simulation mode. In the simulation mode, the SDK library would not communicate
   * with the real backend server, but just returns random mock-up data
   * with requestion data validation.
   *
   * For reference, random mock-up data would be generated by `typia.random<T>()`
   * function.
   *
   * @default false
   */
  simulate?: boolean;

  /**
   * Target directory that e2e test functions would be placed in.
   *
   * If you configure this property and runs `npx nestia e2e` command,
   * `@nestia/sdk` will analyze your NestJS backend server code, and
   * generates e2e test functions for every API endpoints.
   *
   * If not configured, you can't run `npx nestia e2e` command.
   */
  e2e?: string;

  /**
   * Whether to use propagation mode or not.
   *
   * If being configured, interaction functions of the SDK library would
   * perform the propagation mode. The propagation mode means that never
   * throwing exception even when status code is not 200 (or 201), but just
   * returning the {@link IPropagation} typed instance, which can specify its body
   * type through discriminated union determined by status code.
   *
   * @default false
   */
  propagate?: boolean;

  /**
   * Whether to clone DTO structures or not.
   *
   * If being configured, all of DTOs used in the backend server would be cloned
   * into the `structures` directory, and the SDK library would be refer to the
   * cloned DTOs instead of the original.
   *
   * @default false
   */
  clone?: boolean;

  /**
   * Whether to wrap DTO by primitive type.
   *
   * If you don't configure this property as `false`, all of DTOs in the
   * SDK library would be automatically wrapped by {@link Primitive} type.
   *
   * For refenrece, if a DTO type be capsuled by the {@link Primitive} type,
   * all of methods in the DTO type would be automatically erased. Also, if
   * the DTO has a `toJSON()` method, the DTO type would be automatically
   * converted to return type of the `toJSON()` method.
   *
   * @default true
   */
  primitive?: boolean;

  /**
   * Whether to assert parameter types or not.
   *
   * If you configure this property to be `true`, all of the function
   * parameters of SDK library would be checked through
   * [`typia.assert<T>()` function](https://typia.io/docs/validators/assert/).
   *
   * This option would make your SDK library compilation time a little bit slower,
   * but would enahcne the type safety even in the runtime level.
   *
   * @default false
   */
  assert?: boolean;

  /**
   * Whether to optimize JSON string conversion 10x faster or not.
   *
   * If you configure this property to be `true`, the SDK library would utilize the
   * [`typia.assertStringify<T>() function`](https://github.com/samchon/typia#enhanced-json)
   * to boost up JSON serialization speed and ensure type safety.
   *
   * This option would make your SDK library compilation time a little bit slower,
   * but would enhance JSON serialization speed 10x faster. Also, it can ensure type
   * safety even in the runtime level.
   *
   * @default false
   */
  json?: boolean;
}
export namespace INestiaConfig {
  /**
   * List of files or directories to include or exclude to specifying the NestJS
   * controllers.
   */
  export interface IInput {
    /**
     * List of files or directories containing the NestJS controller classes.
     */
    include: string[];

    /**
     * List of files or directories to be excluded.
     */
    exclude?: string[];
  }

  /**
   * Building `swagger.json` is also possible.
   */
  export interface ISwaggerConfig {
    /**
     * Output path of the `swagger.json`.
     *
     * If you've configured only directory, the file name would be the `swagger.json`.
     * Otherwise you've configured the full path with file name and extension, the
     * `swagger.json` file would be renamed to it.
     */
    output: string;

    /**
     * OpenAPI version.
     *
     * If you configure this property to be `2.0` or `3.0`, the newly generated
     * `swagger.json` file would follow the specified OpenAPI version. The newly
     * generated `swagger.json` file would be downgraded from the OpenAPI v3.1
     * specification by {@link OpenApi.downgrade} method.
     *
     * @default 3.1
     */
    openapi?: "2.0" | "3.0" | "3.1";

    /**
     * API information.
     *
     * If omitted, `package.json` content would be used instead.
     */
    info?: Partial<OpenApi.IDocument.IInfo>;

    /**
     * List of server addresses.
     */
    servers?: OpenApi.IServer[];

    /**
     * Security schemes.
     *
     * When generating `swagger.json` file through `nestia`, if your controllers or
     * theirs methods have a security key which is not enrolled in here property,
     * it would be an error.
     */
    security?: Record<string, OpenApi.SecurityScheme>;

    /**
     * List of tag names with description.
     *
     * It is possible to omit this property or skip some tag name even if
     * the tag name is used in the API routes. In that case, the tag name
     * would be used without description.
     *
     * Of course, if you've written a comment like `@tag {name} {description}`,
     * you can entirely replace this property specification.
     */
    tags?: OpenApi.IDocument.ITag[];

    /**
     * Decompose query DTO.
     *
     * If you configure this property to be `true`, the query DTO would be decomposed
     * into individual query parameters per each property. Otherwise you set it to be
     * `false`, the query DTO would be one object type which contains all of query
     * parameters.
     *
     * @default false
     */
    decompose?: boolean;

    /**
     * Operation ID generator.
     *
     * @param props Properties of the API endpoint.
     * @returns Operation ID.
     */
    operationId?(props: {
      class: string;
      function: string;
      method: "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
      path: string;
    }): string;
  }
}
```
  </Tabs.Tab>
</Tabs>

Make `nestia.config.ts` file and run `npx nestia e2e` command.

At first, create `nestia.config.ts` file through `npx nestia init` command. It would be placed on the top level directory of your NestJS backend project. For reference, `tsconfig.json` file also must be placed in the top level directory, too. After creation, configure the `nestia.config.ts` file referencing above example code and type definition.

At least, you've to configure those three properties:

  - `input`: Accessor of controller classes
  - `output`: Path of output directory for SDK library
  - `e2e`: Path of output directory for E2E test functions

When you've completed above configuration, just run `npx nestia e2e` command. Then, [SDK library](./sdk) would be generated into the `$config.output` directory, and E2E test functions would be generated into the `$config.e2e` directory, following your `nestia.config.ts` option.

By the way, `nestia.config.ts` supports alternative options specifying the target controller classes instead of using the `Module` instance. If your backend application server does not have special configuration like `setGlobalPrefix`, `enableVersioning` and `RouterModule`, it is okay to specifying the target controller classes just by writing their file path like below.

<Tabs 
  items={[
    'Module (express)',
    'Module (fastify)',
    'Directory', 
    'Pattern', 
    'Exclude',
  ]}
  defaultIndex={3}>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {7-15}
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";

import { YourModule } from "./src/YourModule";

const NESTIA_CONFIG: INestiaConfig = {
  input: async () => {
    const app = await NestFactory.create(YourModule);
    // app.setGlobalPrefix("api");
    // app.enableVersioning({
    //     type: VersioningType.URI,
    //     prefix: "v",
    // })
    return app;
  },
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {8-16}
import { INestiaConfig } from "@nestia/sdk";
import { NestFactory } from "@nestjs/core";
import { FastifyAdapter } from "@nestjs/platform-fastify";

import { YourModule } from "./src/YourModule";

const NESTIA_CONFIG: INestiaConfig = {
  input: async () => {
    const app = await NestFactory.create(YourModule, new FastifyAdapter());
    // app.setGlobalPrefix("api");
    // app.enableVersioning({
    //     type: VersioningType.URI,
    //     prefix: "v",
    // })
    return app;
  },
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {4}
import { INestiaConfig } from "@nestia/sdk";

const NESTIA_CONFIG: INestiaConfig = {
  input: ["src/controllers", "src/fake/controllers", "src/test/controllers"],
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {4}
import { INestiaConfig } from "@nestia/sdk";

const NESTIA_CONFIG: INestiaConfig = {
  input: "src/**/*.controller.ts",
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript copy filename="nestia.config.ts" showLineNumbers {4-7}
import { INestiaConfig } from "@nestia/sdk";

const NESTIA_CONFIG: INestiaConfig = {
  input: {
    include: ["src/controllers"],
    exclude: ["src/**/*.fake.ts"],
  },
  output: "src/api",
  distribute: "packages/api",
  e2e: "test",
};
export default NESTIA_CONFIG;
```
  </Tabs.Tab>
</Tabs>




## Customization
```typescript filename="test/index.ts" showLineNumbers {8-17}
import core from "@nestia/core";
import { DynamicExecutor } from "@nestia/e2e";

import { INestApplication } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";

async function main(): Promise<void> {
  const server: INestApplication = await NestFactory.create(
    await core.DynamicModule.mount({
      // follows your nestia.config.ts setting
      controllers: {
        input: ["src/controllers"],
        exclude: ["src/**/*.fake.ts"],
      },
    }),
  );
  await server.listen(37_000);

  const report: DynamicExecutor.IReport = await DynamicExecutor.validate({
    prefix: "test",
    parameters: () => [
      {
        host: "http://127.0.0.1:37000",
      },
    ],
  })(`${__dirname}/features`);
  await server.close();

  const exceptions: Error[] = report.executions
    .filter((exec) => exec.error !== null)
    .map((exec) => exec.error!);
  if (exceptions.length === 0) {
    console.log("Success");
    console.log("Elapsed time", report.time.toLocaleString(), `ms`);
  } else {
    for (const exp of exceptions) console.log(exp);
    console.log("Failed");
    console.log("Elapsed time", report.time.toLocaleString(), `ms`);
    process.exit(-1);
  }
}
main().catch((exp) => {
  console.log(exp);
  process.exit(-1);
});
```

Nothing be specified, customize by yourself.

When you generate e2e test functions through `npx nestia e2e` command, such `index.ts` file would be placed into the top level directory of test program. As you can see, the initial e2e test program only opens your NestJS backend server only with path of controllers with port number `37,000`.

However, it may not fully meet your requirements. For example, you may connect to a database server, and also need to configure DI (Dependency Injection) classes, too. You've to configure those things by yourself. `@nestia/sdk` can analyzes your backend server in the compilation level, but unable to reproduce such customizations.

<Tabs 
  items={[
    'Original Controller',
    'SDK Function', 
    'E2E Test Function', 
  ]}
  defaultIndex={2}>
  <Tabs.Tab>
```typescript filename="src/controllers/BbsArticlesController.ts" showLineNumbers
import { Controller } from "@nestjs/common";
import typia, { tags } from "typia";

import core from "@nestia/core";

import { IBbsArticle } from "@api/lib/structures/IBbsArticle";

@Controller("bbs/articles/:section")
export class BbsArticlesController {
  /**
   * Store a new article.
   *
   * @param section Section code
   * @param input Content to store
   * @returns Newly archived article
   */
  @core.TypedRoute.Post()
  public async store(
    @core.TypedParam("section") section: string,
    @core.TypedBody() input: IBbsArticle.IStore,
  ): Promise<IBbsArticle> {
    return {
      ...typia.random<IBbsArticle>(),
      section,
      ...input,
    };
  }

  /**
   * Update an article.
   *
   * @param section Section code
   * @param id Target article ID
   * @param input Content to update
   * @returns Updated content
   */
  @core.TypedRoute.Put(":id")
  public async update(
    @core.TypedParam("section") section: string,
    @core.TypedParam("id") id: string & tags.Format<"uuid">,
    @core.TypedBody() input: IBbsArticle.IStore,
  ): Promise<IBbsArticle> {
    return {
      ...typia.random<IBbsArticle>(),
      id,
      section,
      ...input,
    };
  }
}
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript filename="src/api/functional/bbs/articles/index.ts" showLineNumbers
/**
 * @packageDocumentation
 * @module api.functional.bbs.articles
 * @nestia Generated by Nestia - https://github.com/samchon/nestia
 */
//================================================================
import type { IConnection, Primitive } from "@nestia/fetcher";
import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher";
import type { Format } from "typia/lib/tags/Format";

import type { IBbsArticle } from "../../../structures/IBbsArticle";

/**
 * Store a new article.
 *
 * @param section Section code
 * @param input Content to store
 * @returns Newly archived article
 *
 * @controller [object Object]
 * @path POST /bbs/articles/:section
 * @nestia Generated by Nestia - https://github.com/samchon/nestia
 */
export async function store(
  connection: IConnection,
  section: string,
  input: store.Input,
): Promise<store.Output> {
  return PlainFetcher.fetch(
    {
      ...connection,
      headers: {
        ...connection.headers,
        "Content-Type": "application/json",
      },
    },
    {
      ...store.METADATA,
      path: store.path(section),
    } as const,
    input,
  );
}
export namespace store {
  export type Input = Primitive<IBbsArticle.IStore>;
  export type Output = Primitive<IBbsArticle>;

  export const METADATA = {
    method: "POST",
    path: "/bbs/articles/:section",
    request: {
      type: "application/json",
      encrypted: false,
    },
    response: {
      type: "application/json",
      encrypted: false,
    },
    status: null,
  } as const;

  export const path = (section: string): string => {
    return `/bbs/articles/${encodeURIComponent(section ?? "null")}`;
  };
}

/**
 * Update an article.
 *
 * @param section Section code
 * @param id Target article ID
 * @param input Content to update
 * @returns Updated content
 *
 * @controller [object Object]
 * @path PUT /bbs/articles/:section/:id
 * @nestia Generated by Nestia - https://github.com/samchon/nestia
 */
export async function update(
  connection: IConnection,
  section: string,
  id: string & Format<"uuid">,
  input: update.Input,
): Promise<update.Output> {
  return PlainFetcher.fetch(
    {
      ...connection,
      headers: {
        ...connection.headers,
        "Content-Type": "application/json",
      },
    },
    {
      ...update.METADATA,
      path: update.path(section, id),
    } as const,
    input,
  );
}
export namespace update {
  export type Input = Primitive<IBbsArticle.IStore>;
  export type Output = Primitive<IBbsArticle>;

  export const METADATA = {
    method: "PUT",
    path: "/bbs/articles/:section/:id",
    request: {
      type: "application/json",
      encrypted: false,
    },
    response: {
      type: "application/json",
      encrypted: false,
    },
    status: null,
  } as const;

  export const path = (
    section: string,
    id: string & Format<"uuid">,
  ): string => {
    return `/bbs/articles/${encodeURIComponent(
      section ?? "null",
    )}/${encodeURIComponent(id ?? "null")}`;
  };
}
```
  </Tabs.Tab>
  <Tabs.Tab>
```typescript filename="test/features/api/automated/test_api_bbs_articles_update.ts" showLineNumbers
import type { IConnection, Primitive } from "@nestia/fetcher";
import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher";
import type { Format } from "typia/lib/tags/Format";

import api from "../../../../src/api";
import type { IBbsArticle } from "../../../../src/api/structures/IBbsArticle";

export const test_api_bbs_articles_update = async (
  connection: api.IConnection,
): Promise<void> => {
  const output = await api.functional.bbs.articles.update(
    connection,
    typia.random<string>(),
    typia.random<string & Format<"uuid">>(),
    typia.random<Primitive<IBbsArticle.IStore>>(),
  );
  typia.assert(output);
};
```
  </Tabs.Tab>
</Tabs>

You also need to customize each e2e test functions.

When you run `npx nestia e2e` command, every e2e functions would be placed into `$config.e2e/features/api/automated` directory. Also, automatically generated e2e test functions are composing parameters through [`typia.random<T>()`](https://typia.io/docs/random) function.

If your NestJS backend server development has not been completed, and your API functions are in the mock-up level, such random parameter composition would not be problem. Otherwise your API functions are enoughly completed, such random parameter composition may occur logic error.

Therefore, you also need to customize automatically generated e2e test functions. Move each e2e test files from the `$config.e2e/features/api/automated` directory to somewhere else, and customize those e2e test functions to be suitable for your domain logics.